package tools;

// import java.security.MessageDigest;
// import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class Crypt {

    /*
     * These two functions are for converting between a byte array and
     * a hexadecimal string (16 characters)
     */

    public static String encode(final byte[] IN) {
        final byte[] HEX = "0123456789ABCDEF".getBytes();
        final byte[] OUT = new byte[IN.length * 2];

        for (int i = 0; i < IN.length; i++) {
            OUT[i * 2] = HEX[IN[i] & 0xF];
            OUT[i * 2 + 1] = HEX[IN[i] >> 4 & 0xF];
        }

        return new String(OUT);
    }

    public static byte[] decode(final String IN_STRING) {
        final byte[] IN = IN_STRING.getBytes();
        final byte[] HEX = "0123456789ABCDEF".getBytes();
        final byte[] OUT = new byte[IN.length / 2];

        for (int i = 0; i < OUT.length; i++) {
            OUT[i] |= Arrays.binarySearch(HEX, IN[i * 2]);
            OUT[i] |= Arrays.binarySearch(HEX, IN[i * 2 + 1]) << 4;
        }

        return OUT;
    }

    /*
     * The following functions run a simple xor encryption on a byte
     * array, the key is randomly generated, so it should work
     * relatively well =)
     */

    public static byte[] encrypt(final byte[] PLAIN, final byte[] KEY,
            final int NUMBER) {
        final byte[] CYPHER = new byte[PLAIN.length + NUMBER];

        // Create a random key to encrypt the plain text
        for (int i = PLAIN.length; i < CYPHER.length; i++) {
            CYPHER[i] = (byte) Math.floor(Math.random() * 256);
        }

        // Encrypt the plain text with the random key
        for (int i = 0; i < PLAIN.length; i++) {
            CYPHER[i] = PLAIN[i];
            CYPHER[i] ^= CYPHER[PLAIN.length + i % NUMBER];
        }

        // Encrypt the random key with the given key
        for (int i = PLAIN.length; i < CYPHER.length; i++) {
            CYPHER[i] ^= KEY[i % KEY.length];
        }

        return CYPHER;
    }

    public static byte[] decrypt(final byte[] CYPHER, final byte[] KEY,
            final int NUMBER) {
        final byte[] PLAIN = new byte[CYPHER.length - NUMBER];

        // Decrypt the random key with the given key
        for (int i = PLAIN.length; i < CYPHER.length; i++) {
            CYPHER[i] ^= KEY[i % KEY.length];
        }

        // Decrypt the plain text with the random key
        for (int i = 0; i < PLAIN.length; i++) {
            PLAIN[i] = CYPHER[i];
            PLAIN[i] ^= CYPHER[PLAIN.length + i % NUMBER];
        }

        return PLAIN;
    }

    public static void test() {
        // Debug only, encrypts and decrypts some text
        final byte[] KEY = "example key".getBytes();

        for (int i = 0; i < 4; i++) {
            final String BEFORE = "Just testing the encryption system, note how this is encrypted 4 times using the same key and yet the result is completely different! =D";
            final byte[] DURING = encrypt(BEFORE.getBytes(), KEY, 32);
            final String AFTER = new String(decrypt(DURING, KEY, 32));

            System.out.println("Before: " + BEFORE);
            System.out.println("During: " + new String(DURING));
            System.out.println("After:  " + AFTER);
            System.out.println();
        }
    }

    /*
     * I'm working on adding a hash to the encrypted data so that we
     * can throw an exception if someone tries to modify it. public
     * static byte[] encrypt2(final byte[] PLAIN, final byte[] KEY) {
     * // Pad the data with random final byte[] PLAIN_PADDED = new
     * byte[(int) Math.ceil(PLAIN.length / 16) * 16]; final byte[]
     * CYPHER = new byte[ + 32]; // Generate an MD5 hash of the plain
     * text try { MessageDigest digest =
     * java.security.MessageDigest.getInstance("MD5");
     * digest.update(PLAIN); byte[] hash = digest.digest(); } catch
     * (NoSuchAlgorithmException e) { // Do nothing } // Create a
     * random key to encrypt the plain text for (int i = CYPHER.length
     * - 16; i < CYPHER.length; i++) { CYPHER[i] = (byte)
     * Math.floor(Math.random() * 256); } // Encrypt the plain text
     * with the random key for (int i = 0; i < PLAIN.length; i++) {
     * CYPHER[i] = PLAIN[i]; CYPHER[i] ^= CYPHER[PLAIN.length + i %
     * 16]; } // Encrypt the random key with the given key for (int i
     * = PLAIN.length; i < CYPHER.length; i++) { CYPHER[i] ^= KEY[i %
     * KEY.length]; } return CYPHER; } public static byte[]
     * decrypt2(final byte[] CYPHER, final byte[] KEY) { final byte[]
     * PLAIN = new byte[CYPHER.length - NUMBER]; // Decrypt the random
     * key with the given key for (int i = PLAIN.length; i <
     * CYPHER.length; i++) { CYPHER[i] ^= KEY[i % KEY.length]; } //
     * Decrypt the plain text with the random key for (int i = 0; i <
     * PLAIN.length; i++) { PLAIN[i] = CYPHER[i]; PLAIN[i] ^=
     * CYPHER[PLAIN.length + i % NUMBER]; } return PLAIN; }
     */
}
